//
// Book:      OpenGL(R) ES 2.0 Programming Guide
// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
// ISBN-10:   0321502795
// ISBN-13:   9780321502797
// Publisher: Addison-Wesley Professional
// URLs:      http://safari.informit.com/9780321563835
//            http://www.opengles-book.com
// Additional contributions copyright (c) 2011 Research In Motion Limited

// Simple_VertexShader.c
//
//    This is a simple example that draws a rotating cube in perspective
//    using a vertex shader to transform the object
//
#include <stdlib.h>
#include "esUtil.h"


typedef struct
{
    // Handle to a program object
    GLuint programObject;

    // Attribute locations
    GLint  positionLoc;

    // Uniform locations
    GLint  mvpLoc;

    // Vertex data
    GLfloat  *vertices;
    GLuint   *indices;
    int       numIndices;

    // Rotation angle
    GLfloat   angle;

    // MVP matrix
    ESMatrix  mvpMatrix;
} UserData;

///
// Initialize the shader and program object
//
int Init(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLchar vShaderStr[] =
        "uniform mat4 u_mvpMatrix;                   \n"
        "attribute vec4 a_position;                  \n"
        "void main()                                 \n"
        "{                                           \n"
        "   gl_Position = u_mvpMatrix * a_position;  \n"
        "}                                           \n";

    GLchar fShaderStr[] =
        "precision mediump float;                            \n"
        "void main()                                         \n"
        "{                                                   \n"
        "  gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );        \n"
        "}                                                   \n";

    // Load the shaders and get a linked program object
    userData->programObject = esLoadProgram(vShaderStr, fShaderStr);

    // Get the attribute locations
    userData->positionLoc = glGetAttribLocation(userData->programObject, "a_position");

    // Get the uniform locations
    userData->mvpLoc = glGetUniformLocation(userData->programObject, "u_mvpMatrix");

    // Generate the vertex data
    userData->numIndices = esGenCube(1.0, &userData->vertices, NULL, NULL, &userData->indices);

    // Starting rotation angle for the cube
    userData->angle = 45.0f;

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    return TRUE;
}


///
// Update MVP matrix based on time
//
void Update(ESContext *esContext, float deltaTime)
{
    UserData *userData = (UserData*) esContext->userData;
    ESMatrix perspective;
    ESMatrix modelview;
    float aspect;

    // Compute a rotation angle based on time to rotate the cube
    userData->angle += (deltaTime * 40.0f);
    if (userData->angle >= 360.0f)
        userData->angle -= 360.0f;

    // Compute the window aspect ratio
    aspect = (GLfloat) esContext->width / (GLfloat) esContext->height;

    // Generate a perspective matrix with a 60 degree FOV
    esMatrixLoadIdentity(&perspective);
    esPerspective(&perspective, 60.0f, aspect, 1.0f, 20.0f);

    // Generate a model view matrix to rotate/translate the cube
    esMatrixLoadIdentity(&modelview);

    // Translate away from the viewer
    esTranslate(&modelview, 0.0, 0.0, -2.0);

    // Rotate the cube
    esRotate(&modelview, userData->angle, 1.0, 0.0, 1.0);

    // Compute the final MVP by multiplying the
    // modevleiw and perspective matrices together
    esMatrixMultiply(&userData->mvpMatrix, &modelview, &perspective);
}

///
// Draw a triangle using the shader pair created in Init()
//
void Draw(ESContext *esContext)
{
    UserData *userData = esContext->userData;

    // Set the viewport
    glViewport(0, 0, esContext->width, esContext->height);

    // Clear the color buffer
    glClear(GL_COLOR_BUFFER_BIT);

    // Use the program object
    glUseProgram(userData->programObject);

    // Load the vertex position
    glVertexAttribPointer(userData->positionLoc, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), userData->vertices);

    glEnableVertexAttribArray(userData->positionLoc);

    // Load the MVP matrix
    glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE, (GLfloat*) &userData->mvpMatrix.m[0][0]);

    // Draw the cube
    glDrawElements(GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, userData->indices);

    eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
}

///
// Cleanup
//
void ShutDown(ESContext *esContext)
{
    UserData *userData = esContext->userData;

    if (userData->vertices != NULL)
    {
        free(userData->vertices);
    }

    if (userData->indices != NULL)
    {
        free(userData->indices);
    }

    // Delete program object
    glDeleteProgram(userData->programObject);
}

int main(int argc, char *argv[])
{
    ESContext esContext;
    UserData userData;

    esInitContext(&esContext);
    esContext.userData = &userData;

    esCreateWindow(&esContext, "Simple Texture 2D", 1024, 600, ES_WINDOW_RGB);

    if (!Init(&esContext))
        return 0;

    esRegisterDrawFunc(&esContext, Draw);
    esRegisterUpdateFunc(&esContext, Update);

    esMainLoop(&esContext);

    ShutDown(&esContext);

    return 0;
}
